package org.act.BPEL2Automata.automata.module;

import java.util.LinkedList;
import java.util.Stack;

/**
 * <b>ComplexLogicalFormulaImp</b> uses a tree to hold information.
 * It consists of a left operand,a operator and a right operand.
 * @author baoxiwu<bb@feijiao.info>,ACT,Beihang University
 * @version 0.9.0
 * @since 2010-04-10
 * 
 */
public class ComplexLogicalFormulaImp extends LogicalFormula{
	//operator: AND and OR
	public static final int LOGICAL_FORMULA_OPERATOR_AND = 0;
	public static final int LOGICAL_FORMULA_OPERATOR_OR = 1;
	
	
	//this variable used to contain all the simple logical formula 
	//whose logicalFormulaType = NON_VALID_OR_INVALID_LOGICAL_FORMULA.
	private LinkedList<SimpleLogicalFormulaImp> SLFList = new LinkedList<SimpleLogicalFormulaImp>();
	
	private LogicalFormula leftOperand = null;
	private int operator = ComplexLogicalFormulaImp.LOGICAL_FORMULA_OPERATOR_AND;
	private LogicalFormula rightOperand = null;
	
	
	public ComplexLogicalFormulaImp(){
	//empty
	}
	
	
	public ComplexLogicalFormulaImp(LogicalFormula leftOperand,int operator,LogicalFormula rightOperand){
		if(leftOperand instanceof SimpleLogicalFormulaImp){
			this.SLFList.add((SimpleLogicalFormulaImp) leftOperand);
		}
		else{
			this.SLFList.addAll(((ComplexLogicalFormulaImp) leftOperand).getSLFList());
		}
		
		if(rightOperand instanceof SimpleLogicalFormulaImp){
			this.SLFList.add((SimpleLogicalFormulaImp) rightOperand);
		}
		else{
			this.SLFList.addAll(((ComplexLogicalFormulaImp) rightOperand).getSLFList());
		}

		this.leftOperand = leftOperand;
		this.operator = operator;
		this.rightOperand = rightOperand;
		
		//one ComplexLogicalFormulaImp's key is the same to its leftOperand in default.
		this.setKey(this.leftOperand.getKey());
	}
	
	
	//setter and getter methods.
	/**
	 * get Simple Logical Formula List.It contains all the 
	 * SimpleLogicalFormulaImp objects whose logicalFormulaType 
	 * = NON_VALID_OR_INVALID_LOGICAL_FORMULA 
	 * that exist as leaf nodes of this ComplexLogicalFormulaImp.
	 */
	public LinkedList<SimpleLogicalFormulaImp> getSLFList(){
		return this.SLFList;
	}
	
	/**
	 * select SimpleLogicalFormulaImp objects by their first property,
	 * namely start state.
	 * 
	 */
	public LinkedList<SimpleLogicalFormulaImp> getSLFbyStartState(State start){
		LinkedList<SimpleLogicalFormulaImp> list = new LinkedList<SimpleLogicalFormulaImp>();
		
		for(int i = 0 ; i < this.getSLFList().size(); i++){
			if(this.getSLFList().get(i).getStartState() == start){
				list.add(this.getSLFList().get(i));
			}
		}
		
		return list;
	}
	
	public void setLeftOperand(LogicalFormula leftOperand){
		if(leftOperand instanceof SimpleLogicalFormulaImp){
			this.SLFList.add((SimpleLogicalFormulaImp) leftOperand);
		}
		else{
			this.SLFList.addAll(((ComplexLogicalFormulaImp) leftOperand).getSLFList());
		}
		
		this.leftOperand = leftOperand;
	}
	
	public LogicalFormula getLeftOperand(){
		return this.leftOperand;
	}
	
	public void setOperator(int operator){
		this.operator = operator;
	}
	
	public int getOperator(){
		return this.operator;
	}
	
	public void setRightOperand(LogicalFormula rightOperand){
		if(rightOperand instanceof SimpleLogicalFormulaImp){
			this.SLFList.add((SimpleLogicalFormulaImp) rightOperand);
		}
		else{
			this.SLFList.addAll(((ComplexLogicalFormulaImp) rightOperand).getSLFList());
		}
		
		this.rightOperand = rightOperand;
	}
	
	public LogicalFormula getRightOperand(){
		return this.rightOperand;
	}
	
	
	//TODO TESTING WANTED.
	public boolean calculate(){
		boolean left = false,right = false;
		
		//calculate the value of left operand
		if(this.getLeftOperand() instanceof SimpleLogicalFormulaImp){
			SimpleLogicalFormulaImp slf = (SimpleLogicalFormulaImp) this.getLeftOperand();
			left = slf.calculate(); 
		}
		else{
			ComplexLogicalFormulaImp clf = (ComplexLogicalFormulaImp) this.getLeftOperand();
			left = clf.calculate();
		}
		
		//calculate the value of right operand
		if(this.getRightOperand() instanceof SimpleLogicalFormulaImp){
			SimpleLogicalFormulaImp slf = (SimpleLogicalFormulaImp) this.getRightOperand();
			right = slf.calculate(); 
		}
		else{
			ComplexLogicalFormulaImp clf = (ComplexLogicalFormulaImp) this.getRightOperand();
			right = clf.calculate();
		}
		
		//calculate the value of this.
		if(this.getOperator() == ComplexLogicalFormulaImp.LOGICAL_FORMULA_OPERATOR_AND){
			if((left == true) && (right == true)){
				return true;
			}
			else{
				return false;
			}
		}
		else{
			if((left == false) && (right == false)){
				return false;
			}
			else{
				return true;
			}
		}
	}
	
	
	//implementation by using non-recursive algorithm
	public void update(State state,State newState){
		Stack<LogicalFormula> stack = new Stack<LogicalFormula>();
		
		stack.push(this);
		LogicalFormula temp = null;
		
		while(true){
			if(temp != null){
				if(temp instanceof ComplexLogicalFormulaImp){
					//TODO TESTING WANTED.
					temp.setKey(newState);
					
					stack.push(((ComplexLogicalFormulaImp)temp).getLeftOperand());
					stack.push(((ComplexLogicalFormulaImp)temp).getRightOperand());
					
					temp = null;
				}
				else{
					SimpleLogicalFormulaImp slf = (SimpleLogicalFormulaImp)temp;
					
					System.out.println("@CLFI.update() slf.getStartState() = s" + slf.getStartState().getID());
					
					if(slf.getStartState() == state){
						slf.setKey(newState);
						slf.setStartState(newState);
					}
					
					if(stack.size() > 0){
						temp = stack.pop();
					}
					else{
						temp = null;
					}
				}
			}
			else
			if(temp == null && stack.size() > 0){
				temp = (LogicalFormula)stack.pop();
			}
			else{
				break;
			}
		}
	}
	
	
	//clone a copy and update it,'cause one logical formula may be referenced by one or more states.
	//modify its properties may lead to a error.
	//implemented by using recursive algorithm
	public LogicalFormula updateACopy(LogicalFormula lf, State newState){
		//handle simple logical formula or the so-called leaf nodes.
		if(lf instanceof SimpleLogicalFormulaImp){
			return ((SimpleLogicalFormulaImp)lf).updateACopy(newState);
		}
		//handle complex logical formula.
		else{
			ComplexLogicalFormulaImp clf = (ComplexLogicalFormulaImp)lf;
			
			LogicalFormula llf = updateACopy(clf.getLeftOperand(),newState);
			LogicalFormula rlf = updateACopy(clf.getRightOperand(),newState);
			
			ComplexLogicalFormulaImp newclf = new ComplexLogicalFormulaImp(llf,clf.getOperator(),rlf);
			
			return newclf;
		}
	}
	
	
	//recursive algorithm.
	//designed for SubAutomataImp.doShuffle() 
	public LogicalFormula updateACopy(LogicalFormula lf,LinkedList<State> stateSet,DualState newState,int matchType){
		//handle simple logical formula or the so-called leaf nodes.
		if(lf instanceof SimpleLogicalFormulaImp){
			return ((SimpleLogicalFormulaImp)lf).updateACopy(stateSet,newState,matchType);
		}
		//handle complex logical formula.
		else{
			ComplexLogicalFormulaImp clf = (ComplexLogicalFormulaImp)lf;
			
			LogicalFormula llf = updateACopy(clf.getLeftOperand(),stateSet,newState,matchType);
			LogicalFormula rlf = updateACopy(clf.getRightOperand(),stateSet,newState,matchType);
			
			ComplexLogicalFormulaImp newclf = new ComplexLogicalFormulaImp(llf,clf.getOperator(),rlf);
			
			return newclf;
		}
	}
	
	
	public String toString(){
		Stack<LogicalFormula> stack = new Stack<LogicalFormula>();
		String result = "";
		int table = 0;
		int flag = 0;
		
		stack.push(this);
		LogicalFormula temp = null;
		
		while(true){
			if(temp != null){
				if(temp instanceof ComplexLogicalFormulaImp){
					stack.push(((ComplexLogicalFormulaImp)temp).getLeftOperand());
					table++;
					
					for(int i = 0; i < table; i++){
						result += "\t";
					}
					
					result += ((((ComplexLogicalFormulaImp)temp).getOperator() ==  ComplexLogicalFormulaImp.LOGICAL_FORMULA_OPERATOR_AND?"AND":"OR")+ "\n");
					
					stack.push(((ComplexLogicalFormulaImp)temp).getRightOperand());
					//table++;
					
					temp = null;
					
					flag = table;
				}
				else{
					for(int i = 0; i < table; i++){
						result += "\t";
					}
					
					result += (((SimpleLogicalFormulaImp)temp).toString());
					
					if(stack.size() > 0){
						temp = stack.pop();
						
						//flag = table;
						flag--;
						if((table - flag)%2 == 0){
							table--;
							flag = table;;
						}
					}
					else{
						temp = null;
					}
				}
			}
			else
			if(temp == null && stack.size() > 0){
				temp = (LogicalFormula)stack.pop();
				
				/*
				if(table % 2 != 0){
					table--;
				}
				*/
			}
			else{
				break;
			}
		}
		
		return result;
	}
}
